
/*
 * @项目名称: 模板开发
 * @FilePath: /meek/src/source/tile/TileQueue.js
 * @Author: 
 * @Date: 2021-01-29T16:53:45+08:00
 * @LastEditors: kongxueyang@datatang.com
 * @LastEditTime: 2021-05-06T19:15:05+08:00
 * Copyright (c) 2018-2022
 */

/**
 * @module ol/TileQueue
 */
import TileState from './TileState.js';
import EventType from '../../events/EventType.js';
import PriorityQueue from '../../structs/PriorityQueue.js';
import Animation from '../../utils/Animation.js';

/**
 * @typedef {function(import("./Tile.js").default, string, import("./coordinate.js").Coordinate, number): number} PriorityFunction
 */


class TileQueue extends PriorityQueue {

  /**
   * @param {PriorityFunction} tilePriorityFunction Tile priority function.
   * @param {function(): ?} tileChangeCallback Function called on each tile change event.
   */
  constructor(tilePriorityFunction, tileChangeCallback) {

    super(
      /**
       * @param {Array} element Element.
       * @return {number} Priority.
       */
      function(element) {
        return tilePriorityFunction.apply(null, element);
      },
      /**
       * @param {Array} element Element.
       * @return {string} Key.
       */
      function(element) {
        return (/** @type {import("./Tile.js").default} */ (element[0]).getKey());
      });

    /** @private */
    this.boundHandleTileChange_ = this.handleTileChange.bind(this);

    /**
     * @private
     * @type {function(): ?}
     */
    this.tileChangeCallback_ = tileChangeCallback;

    /**
     * @private
     * @type {number}
     */
    this.tilesLoading_ = 0;

    /**
     * @private
     * @type {!Object<string,boolean>}
     */
    this.tilesLoadingKeys_ = {};

    this.animation = new Animation(5, () => {
      this.loadPreTiles();
    });


  }

  /**
   * @inheritDoc
   */
  enqueue(element) {
    const added = super.enqueue(element);
    if (added) {
      const tile = element[0];
      tile.addEventListener(EventType.CHANGE, this.boundHandleTileChange_);
    }
    return added;
  }

  /**
   * @return {number} Number of tiles loading.
   */
  getTilesLoading() {
    return this.tilesLoading_;
  }

  /**
   * @param {import("./events/Event.js").default} event Event.
   * @protected
   */
  handleTileChange(event) {
    const tile = /** @type {import("./Tile.js").default} */ (event.target);
    const state = tile.getState();
    if (tile.hifi && state === TileState.LOADED || state === TileState.ERROR ||
        state === TileState.EMPTY || state === TileState.ABORT) {
      tile.removeEventListener(EventType.CHANGE, this.boundHandleTileChange_);
      const tileKey = tile.getKey();
      if (tileKey in this.tilesLoadingKeys_) {
        delete this.tilesLoadingKeys_[tileKey];
        --this.tilesLoading_;
      }
      this.tileChangeCallback_();
    }
  }

  /**
   * @param {number} maxTotalLoading Maximum number tiles to load simultaneously.
   * @param {number} maxNewLoads Maximum number of new tiles to load.
   */
  loadMoreTiles(maxTotalLoading, maxNewLoads) {
    this.animation.pause();
    let newLoads = 0;
    let abortedTiles = false;
    let state, tile, tileKey;
    let preloadlist = [];
    let loadedList = [];
    while (this.tilesLoading_ < maxTotalLoading && newLoads < maxNewLoads &&
           this.getCount() > 0) {
      let data = this.dequeue();
      tile = /** @type {import("./Tile.js").default} */ (data[0]);
      tileKey = tile.getKey();
      state = tile.getState();
      if (data[4] && data[4].preload) {
        preloadlist.push(data);
        continue ;
      }
      if (state === TileState.ABORT) {
        abortedTiles = true;
      } else if (state === TileState.IDLE && !(tileKey in this.tilesLoadingKeys_)) {
        this.tilesLoadingKeys_[tileKey] = true;
        ++this.tilesLoading_;
        ++newLoads;
        console.warn('load：' + tileKey)
        let loadR = tile.load();

        if (loadR && loadR.then) {
          loadedList.push(loadR);
        }
      }
    }
    if (newLoads === 0 && abortedTiles) {
      // Do not stop the render loop when all wanted tiles were aborted due to
      // a small, saturated tile cache.
      this.tileChangeCallback_();
    }
    this.preloadlist = preloadlist;
    Promise.allSettled(loadedList).then(() => {
      this.animation.start();
    })
  }

  loadPreTiles() {
    let data = this.preloadlist.pop();
    if (!data) {
      return this.animation.pause();
    }
    let tile = /** @type {import("./Tile.js").default} */ (data[0]);
    let tileKey = tile.getKey();
    let state = tile.getState();
    if (state === TileState.ABORT) {
      abortedTiles = true;
    } else if (state === TileState.IDLE && !(tileKey in this.tilesLoadingKeys_)) {
      this.tilesLoadingKeys_[tileKey] = true;
      ++this.tilesLoading_;
      this.animation.pause();
      const loadR = tile.load();
      if (loadR && loadR.then) {
        loadR.finally(() => {
          console.warn('preload：' + tileKey)
          this.animation.start();
        })
      } else {
        this.animation.start();
      }
    }
  }
}


export default TileQueue;
